﻿using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.EventArgs.Interfaces;
using Exiled.Events.EventArgs.Player;
using Exiled.Events.Features;
using MEC;
using PlayerStatsSystem;
using RgCore.PlayerHud;
using System;
using System.Collections.Generic;
using System.Linq;
using RgCore.CustomSide;

namespace RgCore.Events
{
    public class DamageResultingEventArgs : IExiledEvent, IPlayerEvent
    {
        public DamageResultingEventArgs(Exiled.API.Features.Player target, PlayerStatsSystem.DamageHandlerBase damageHandler)
        {
            DamageHandler = damageHandler;
            Attacker = DamageHandler is PlayerStatsSystem.AttackerDamageHandler adh ? Player.Get(adh.Attacker) : null;
            Player = target;
        }

        public Player Player { get; }

        public Player Attacker { get; }

        public PlayerStatsSystem.DamageHandlerBase DamageHandler { get; set; }
    }

    public abstract class CustomEvent<T> : IExiledEvent where T : IExiledEvent
    {
        protected abstract event CustomEventHandler<T> InnerEvent;

        public void Subscribe(CustomEventHandler<T> handler)
        {
            InnerEvent += handler;
        }

        public void Unsubscribe(CustomEventHandler<T> handler)
        {
            InnerEvent -= handler;
        }

        public abstract void InvokeSafely(T ev);
    }

    public class HurtingEvent : CustomEvent<HurtingEventArgs>
    {
        protected override event CustomEventHandler<HurtingEventArgs> InnerEvent;

        public override void InvokeSafely(HurtingEventArgs ev)
        {
            if (ev.DamageHandler.Base is AttackerDamageHandler && ev.Attacker != ev.Player)
            {
                var attacker = ev.Attacker.IsAlive || !EventProcessor.AliveRoleSide.TryGetValue(ev.Attacker.ReferenceHub, out var side) ? ev.Attacker.GetSide() : side;
                var target = ev.Player.GetSide();
                if (attacker.IsFriendly(target) && !attacker.FriendlyFire)
                {
                    ev.IsAllowed = false;
                    ev.Amount = 0;
                    return;
                }
            }
            if (this.InnerEvent == null)
                return;
            foreach (CustomEventHandler<HurtingEventArgs> item in this.InnerEvent.GetInvocationList().Cast<CustomEventHandler<HurtingEventArgs>>())
            {
                try
                {
                    item(ev);
                    if (!ev.IsAllowed)
                        break;
                }
                catch (Exception ex)
                {
                    Log.Error($"Method \"{item.Method.Name}\" of the class \"{item.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
                }
            }
        }
    }

    public class DyingEvent : CustomEvent<DyingEventArgs>
    {
        protected override event CustomEventHandler<DyingEventArgs> InnerEvent;

        public override void InvokeSafely(DyingEventArgs ev)
        {
            if (this.InnerEvent == null)
                return;
            foreach (CustomEventHandler<DyingEventArgs> item in this.InnerEvent.GetInvocationList().Cast<CustomEventHandler<DyingEventArgs>>())
            {
                try
                {
                    item(ev);
                    if (!ev.IsAllowed)
                        break;
                }
                catch (Exception ex)
                {
                    Log.Error($"Method \"{item.Method.Name}\" of the class \"{item.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
                }
            }
            if (ev.IsAllowed)
            {
                if (!EventProcessor.AliveRoleSide.ContainsKey(ev.Player.ReferenceHub))
                {
                    EventProcessor.AliveRoleSide.Add(ev.Player.ReferenceHub, null);
                }
                EventProcessor.AliveRoleSide[ev.Player.ReferenceHub] = ev.Player.GetSide();
            }
        }
    }

    public class DamageResultingEvent : CustomEvent<DamageResultingEventArgs>
    {
        protected override event CustomEventHandler<DamageResultingEventArgs> InnerEvent;

        public override void InvokeSafely(DamageResultingEventArgs ev)
        {
            if (this.InnerEvent == null)
                return;
            foreach (CustomEventHandler<DamageResultingEventArgs> item in this.InnerEvent.GetInvocationList().Cast<CustomEventHandler<DamageResultingEventArgs>>())
            {
                try
                {
                    item(ev);
                }
                catch (Exception ex)
                {
                    Log.Error($"Method \"{item.Method.Name}\" of the class \"{item.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
                }
            }
        }
    }

    public static class EventProcessor
    {
        // 累计伤害，Dictionary<受到伤害的玩家, <造成伤害的玩家，伤害>>
        public static Dictionary<Player, Dictionary<Player, float>> Damages = new Dictionary<Player, Dictionary<Player, float>>();

        public static DamageResultingEvent DamageResulting { get; set; } = new DamageResultingEvent();

        public static HurtingEvent OnHurting { get; set; } = new HurtingEvent();

        public static DyingEvent OnDying { get; set; } = new DyingEvent();

        public static DamageResultingEvent ReadyDying { get; set; } = new DamageResultingEvent();
        public static Dictionary<ReferenceHub, CustomSide.CustomSide> AliveRoleSide = new Dictionary<ReferenceHub, CustomSide.CustomSide>();

        public static void Start()
        {
            Exiled.Events.Handlers.Player.Hurting += Player_Hurting;
            Exiled.Events.Handlers.Server.RestartingRound += Server_RestartingRound;
            Exiled.Events.Handlers.Player.Dying += Player_Dying;
        }

        private static void Player_Dying(DyingEventArgs ev)
        {
            OnDying.InvokeSafely(ev);
            if (ev.IsAllowed)
            {
                if (Damages.TryGetValue(ev.Player, out var list))
                {
                    List<string> text = ListPool<string>.Pool.Get();
                    text.Add("你生前受到了以下玩家的伤害：");
                    var damage = list.ToList();
                    DictionaryPool<Player, float>.Pool.Return(list);
                    damage.Sort((x , y) => -x.Value.CompareTo(y.Value));
                    int i = 0;
                    for (; i < 4 && i < damage.Count; i++)
                    {
                        var item = damage[i];
                        int percent = (int)(Math.Round(item.Value / ev.Player.MaxHealth, 2) * 100);
                        text.Add($"<color=yellow>{item.Key.Nickname}</color> [<color=red>{percent}%</color>]");
                    }
                    int remain = damage.Count - i - 1;
                    if (remain > 0)
                        text.Add($"<color=#777777>还有其他 {remain} 名玩家对你造成过伤害</color>");
                    Damages.Remove(ev.Player);
                    ev.Player.SendRawMsg("damage", ListPool<string>.Pool.ToArrayReturn(text), 7, ScreenLocation.CenterBottom, false);
                }
                DamageResultingEventArgs ea = new DamageResultingEventArgs(ev.Player, ev.DamageHandler.Base);
                ReadyDying.InvokeSafely(ea);
            }
        }
        private static void Server_RestartingRound()
        {
            Timing.CallDelayed(1.0f, () =>
            {
                AliveRoleSide.Clear();
                foreach (var damage in Damages)
                {
                    DictionaryPool<Player, float>.Pool.Return(damage.Value);
                }
                Damages.Clear();
            });
            
        }

        private static void Player_Hurting(HurtingEventArgs ev)
        {
            if (ev.Player.Role.Team == PlayerRoles.Team.SCPs)
            {
                ev.Amount *= RgCore.instance.Config.ScpDealtDamageMultiplier;
            }
            OnHurting.InvokeSafely(ev);
            if (ev.IsAllowed && ev.Amount != 0f)
            {
                if (ev.Attacker != null)
                {
                    if (!Damages.ContainsKey(ev.Player))
                    {
                        Damages.Add(ev.Player, DictionaryPool<Player, float>.Pool.Get());
                    }
                    if (!Damages[ev.Player].ContainsKey(ev.Attacker))
                    {
                        Damages[ev.Player].Add(ev.Attacker, 0f);
                    }
                    Damages[ev.Player][ev.Attacker] += ev.Amount;
                }
                DamageResultingEventArgs ea = new DamageResultingEventArgs(ev.Player, ev.DamageHandler.Base);
                DamageResulting.InvokeSafely(ea);
            }
        }
    }
}